package org.igroman.wallpaper.live.boxes;

import java.util.Iterator;
import java.util.Random;

import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.engine.options.EngineOptions;
import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.anddev.andengine.engine.options.resolutionpolicy.FillResolutionPolicy;
import org.anddev.andengine.entity.primitive.Rectangle;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
import org.anddev.andengine.entity.scene.background.ColorBackground;
import org.anddev.andengine.entity.shape.Shape;
import org.anddev.andengine.entity.sprite.TiledSprite;
import org.anddev.andengine.entity.util.FPSCounter;
import org.anddev.andengine.extension.physics.box2d.PhysicsConnector;
import org.anddev.andengine.extension.physics.box2d.PhysicsFactory;
import org.anddev.andengine.extension.physics.box2d.PhysicsWorld;
import org.anddev.andengine.extension.physics.box2d.util.Vector2Pool;
import org.anddev.andengine.extension.ui.livewallpaper.BaseLiveWallpaperService;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.anddev.andengine.sensor.accelerometer.AccelerometerData;
import org.anddev.andengine.sensor.accelerometer.IAccelerometerListener;

import android.content.Context;
import android.hardware.SensorManager;
import android.view.Display;
import android.view.Menu;
import android.view.WindowManager;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.Manifold;

class BodyUserData {
	// Fields
	TiledSprite tiledSprite;
	// Constructor
	BodyUserData(final TiledSprite pTiledSprite) {
		this.tiledSprite = pTiledSprite;
	}
}

public class iphyswallpaper extends BaseLiveWallpaperService implements IAccelerometerListener, IOnSceneTouchListener {
	
	private static int CAMERA_WIDTH = 320;
	private static int CAMERA_HEIGHT = 480;
	private static final FixtureDef FIXTURE_DEF = PhysicsFactory.createFixtureDef(1, 0.5f, 0.5f);
	
	private Camera cCamera;
	private BitmapTextureAtlas cBitmapTextureAtlas;
	private TiledTextureRegion cFaceTextureRegion;
	private Scene cScene;
	private PhysicsWorld cPhysicsWorld;
	private ScreenOrientation cScreenOrientation;
	private static final float ForceImpuse = -5;

	@Override
	public org.anddev.andengine.engine.Engine onLoadEngine() {
		WindowManager window = (WindowManager) this.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
		Display display = window.getDefaultDisplay();
		CAMERA_WIDTH = display.getWidth();
		CAMERA_HEIGHT = display.getHeight();
		cCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		return new org.anddev.andengine.engine.Engine(new EngineOptions(true, cScreenOrientation, new FillResolutionPolicy(), cCamera));
	}

	@Override
	public void onLoadResources() {
		cBitmapTextureAtlas = new BitmapTextureAtlas(64, 32, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
		cFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(cBitmapTextureAtlas, this, "face_box_tiled.png", 0, 0, 2, 1);
		this.getEngine().getTextureManager().loadTexture(cBitmapTextureAtlas);
	}

	@Override
	public Scene onLoadScene() {
		
		cScene = new Scene();
		cScene.setBackground(new ColorBackground(0, 0, 0));
		this.cScene.setOnSceneTouchListener(this);
		this.enableAccelerometerSensor(this);
		this.cScene.setTouchAreaBindingEnabled(true);
		
		cPhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false);
		
		this.cPhysicsWorld.setContactListener(new ContactListener() {
            @Override
            public void beginContact(final Contact pContact) {
                    Body body = pContact.getFixtureA().getBody();
                    BodyUserData userdata = (BodyUserData) body.getUserData();
                    if (userdata != null) userdata.tiledSprite.setCurrentTileIndex(0);

                    body = pContact.getFixtureB().getBody();
                    userdata = (BodyUserData) body.getUserData();
                    if (userdata != null) userdata.tiledSprite.setCurrentTileIndex(0);
            }

            @Override
            public void endContact(final Contact pContact) {
            	Body body = pContact.getFixtureA().getBody();
                BodyUserData userdata = (BodyUserData) body.getUserData();
                if (userdata != null) userdata.tiledSprite.setCurrentTileIndex(1);

                body = pContact.getFixtureB().getBody();
                userdata = (BodyUserData) body.getUserData();
                if (userdata != null) userdata.tiledSprite.setCurrentTileIndex(1);
            }

			@Override
			public void postSolve(Contact arg0, ContactImpulse arg1) {				
			}

			@Override
			public void preSolve(Contact arg0, Manifold arg1) {				
			}
		});
		
		
		final Shape ground = new Rectangle(0, CAMERA_HEIGHT - 2, CAMERA_WIDTH, 2);
		final Shape roof = new Rectangle(0, 0, CAMERA_WIDTH, 2);
		final Shape left = new Rectangle(0, 0, 2, CAMERA_HEIGHT);
		final Shape right = new Rectangle(CAMERA_WIDTH - 2, 0, 2, CAMERA_HEIGHT);
		
		final FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0, 0.5f, 0.5f);
		PhysicsFactory.createBoxBody(this.cPhysicsWorld, ground, BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.cPhysicsWorld, roof, BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.cPhysicsWorld, left, BodyType.StaticBody, wallFixtureDef);
		PhysicsFactory.createBoxBody(this.cPhysicsWorld, right, BodyType.StaticBody, wallFixtureDef);
		
		cScene.attachChild(ground);
		cScene.attachChild(roof);
		cScene.attachChild(left);
		cScene.attachChild(right);
		
		cScene.registerUpdateHandler(cPhysicsWorld);
		
		Random random = new Random();
		
		for (int i = 0; i < 15; i++)
		 addFace((float) 50 + random.nextInt(10), (float) 50 + random.nextInt(10));
		
		return cScene;		
	}

	@Override
	public void onLoadComplete() {		
	}
	
	@Override
	public boolean onSceneTouchEvent(final Scene pScene, final TouchEvent pSceneTouchEvent) {
		if (cPhysicsWorld != null) {
			if (pSceneTouchEvent.isActionDown()) {
				Iterator<Body> bodies = cPhysicsWorld.getBodies();
				while (bodies.hasNext()) {
					 impulse(bodies.next(), pSceneTouchEvent.getX(), pSceneTouchEvent.getY());
				}
				return true;
			}
		}
		return false;
	}

	@Override
	public void onAccelerometerChanged(final AccelerometerData pAccelerometerData) {
		final Vector2 gravity = Vector2Pool.obtain(pAccelerometerData.getX(), pAccelerometerData.getY());
		cPhysicsWorld.setGravity(gravity);
		Vector2Pool.recycle(gravity);
	}
	

	private void addFace(final float pX, final float pY) {
		final TiledSprite face;
		final Body body;
		
		face = new TiledSprite(pX, pY, this.cFaceTextureRegion.clone());
		face.setCurrentTileIndex(1);
		body = PhysicsFactory.createBoxBody(cPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF);
		body.setUserData(new BodyUserData(face));
		face.setUserData(body);
		
		cScene.attachChild(face);
		cPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(face, body));
	}
	
	private void impulse(final Body body, final float pX, final float pY) {		
		final Vector2 velocity = Vector2Pool.obtain(new Vector2(body.getPosition().x - pX, body.getPosition().x - pY).nor().mul(ForceImpuse));
		body.applyAngularImpulse(ForceImpuse);
		Vector2Pool.recycle(velocity);
	}

	@Override
	public void onUnloadResources() {
	}

	@Override
	public void onPauseGame() {
		super.onPause();
	}

	@Override
	public void onResumeGame() {
		super.onResume();
	}
}